<script lang="ts">
export default {
  name: "LayLayer",
};
</script>

<script lang="ts" setup>
import { Ref, ref, watch, computed, useSlots, VNodeTypes, isVNode, render } from "vue";
import {
  nextId,
  nextIndex,
  maxArea,
  maxOffset,
  getArea,
  getPosition,
  calculateArea,
  calculateOffset,
  calculateContent,
  calculateType,
  minArea,
  minOffset,
  updateMinArrays,
  getDrawerAnimationClass,
  calculateDrawerArea,
} from "../../tools";
import useMove from "../../tools/useMove";

// 辅助支持 TODO
const slot = useSlots();
const layero = ref(null);
const emit = defineEmits(["close", "update:modelValue"]);

// 传递属性 TODO
export interface LayModalProps {
  domId?: string | string;
  title?: string | boolean | Function
  icon?: string | number;
  skin?: string;
  zIndex?: number | Function;
  setTop?: boolean;
  offset?: string[] | string;
  area?: string[] | "auto";
  modelValue?: boolean;
  maxmin?: boolean | string;
  btn?: Record<string, unknown>[] | false;
  move?: boolean | string;
  resize?: boolean | string;
  type?: 0 | 1 | 2 | 3 | "dialog" | "page" | "iframe" | "loading" | "drawer";
  content?: string | Function | object | VNodeTypes
  isHtmlFragment?: boolean;
  shade?: boolean | string;
  shadeClose?: boolean | string;
  shadeOpacity?: string;
  closeBtn?: boolean | string;
  btnAlign?: "l" | "c" | "r";
  time?: number;
  load?: number;
  anim?: 0 | 1 | 2 | 3 | 4 | 5 | 6;
  isOutAnim?: boolean;
  destroy?: Function;
  success?: Function;
  end?: Function;
  yes?: Function;
  yesText?: string;
  isFunction?: boolean;
  isMessage?: boolean;
  appContext?: any;
}

const props = withDefaults(defineProps<LayModalProps>(), {
  title: "信息",
  zIndex: () => nextIndex(),
  setTop: false,
  offset: () => ["50%", "50%"],
  area: "auto",
  modelValue: false,
  maxmin: false,
  move: true,
  type: 1,
  time: 0,
  shade: true,
  shadeClose: true,
  shadeOpacity: "0.1",
  closeBtn: true,
  btnAlign: "r",
  load: 0,
  anim: 0,
  resize: false,
  isHtml: false,
  isOutAnim: true,
  destroy: () => { },
  success: () => { },
  end: () => { },
  yesText: "确定",
  isFunction: false,
  isMessage: false,
});

// 初始属性
const id: Ref<string> = ref(nextId());
const max: Ref<boolean> = ref(false);
const min: Ref<boolean> = ref(false);
const type: number = calculateType(props.type);
const area: Ref<string[]> = props.type != "drawer" ? ref(calculateArea(props.area)) : ref(calculateDrawerArea(props.offset, props.area));
const offset: Ref<string[]> = ref(calculateOffset(props.offset, area, props.type));
const contentHeight = ref(calculateContent(area.value[1], props.btn, type));
const index: Ref<number | Function> = ref(props.zIndex);
const visible: Ref<boolean> = ref(false);
const first: Ref<boolean> = ref(true);

// 实时位置
const w: Ref<string> = ref(area.value[0]);
const h: Ref<string> = ref(area.value[1]);
const t: Ref<string> = ref(offset.value[0]);
const l: Ref<string> = ref(offset.value[1]);

// 原始位置
const _w: Ref<string> = ref(area.value[0]);
const _h: Ref<string> = ref(area.value[0]);
const _t: Ref<string> = ref(offset.value[0]);
const _l: Ref<string> = ref(offset.value[1]);

// 首次打开 TODO
const firstOpenDelayCalculation = function () {
  setTimeout(() => {
    area.value = getArea(layero.value);
    offset.value = calculateOffset(props.offset, area.value, props.type);
    w.value = area.value[0];
    h.value = area.value[1];
    t.value = offset.value[0];
    l.value = offset.value[1];
    _w.value = area.value[0];
    _l.value = area.value[1];
    _t.value = offset.value[0];
    _l.value = offset.value[1];
    index.value = nextIndex();
    supportMove();
  }, 20);
};

// 恢复打开 TODO
const notFirstOpenLayerInit = function () {
  w.value = _w.value;
  h.value = _h.value;
  t.value = _t.value;
  l.value = _l.value;
  index.value = nextIndex();
  setTimeout(() => {
    supportMove();
  }, 20);

};

// 关闭之前
const beforeCloseSaveData = function () {
  if (min.value) {
    minHandle();
  } else if (max.value) {
    maxHandle();
  } else {
    _w.value = getArea(layero.value)[0];
    _h.value = getArea(layero.value)[1];
    _t.value = getPosition(layero.value).y;
    _l.value = getPosition(layero.value).x;
  }
};

// 关闭事件
const shadeHandle = () => {
  if (props.shadeClose) closeHandle();
};

// 关闭事件
const closeHandle = () => {
  emit("close");
  emit("update:modelValue", false);
  props.destroy();
};

// 确认事件
const yesHandle = () => {
  if (props.yes != undefined) props.yes();
  else closeHandle();
};

// 最大化事件
const maxHandle = () => {
  if (max.value) {
    // 恢复位置
    w.value = _w.value;
    h.value = _h.value;
    t.value = _t.value;
    l.value = _l.value;
  } else {
    // 存储位置
    _t.value = getPosition(layero.value).y;
    _l.value = getPosition(layero.value).x;
    _w.value = getArea(layero.value)[0];
    _h.value = getArea(layero.value)[1];
    // 最大赋值
    w.value = maxArea().w;
    h.value = maxArea().h;
    t.value = maxOffset().t;
    l.value = maxOffset().l;
  }
  contentHeight.value = calculateContent(h.value, props.btn, type);
  max.value = !max.value;
};

// 最小化事件
const minHandle = () => {
  let left = 180 * updateMinArrays(id.value, !min.value);
  if (left > document.documentElement.clientWidth - 180) {
    left = document.documentElement.clientWidth - 180;
  }
  if (min.value) {
    // 恢复位置
    w.value = _w.value;
    h.value = _h.value;
    t.value = _t.value;
    l.value = _l.value;
  } else {
    // 存储位置
    _w.value = getArea(layero.value)[0];
    _h.value = getArea(layero.value)[1];
    _t.value = getPosition(layero.value).x;
    _l.value = getPosition(layero.value).y;
    t.value = minOffset(left).t;
    l.value = minOffset(left).l;
    h.value = minArea().h;
    w.value = minArea().w;
  }
  contentHeight.value = calculateContent(h.value, props.btn, type);
  min.value = !min.value;
};

// 置顶操作
const setTopHandle = () => {
  index.value = nextIndex();
};

// 拖拽支持
const supportMove = function () {
  if (props.move && props.type != "drawer") {
    if (layero.value != null) {
      // @ts-ignore
      useMove(layero.value, (width, height, left, top) => {
        // 更改实时属性
        contentHeight.value = calculateContent(h.value, props.btn, type);
        h.value = height;
        w.value = width;
        l.value = left;
        t.value = top;
      });
    }
  }
};

// 窗体位置
const styles = computed(() => {
  return {
    top: t.value,
    left: l.value,
    width: w.value,
    height: h.value,
    zIndex: index.value,
  };
});

// 入场动画
const enterActiveClass = computed(() => {
  if(props.type === "drawer"){
    return getDrawerAnimationClass(props.offset)
  }
  return `layer-anim layer-anim-0${props.anim}`;
});

// 出场动画
const leaveActiveClass = computed(() => {
  if(props.type === "drawer"){
    return getDrawerAnimationClass(props.offset,true);
  }
  return props.isOutAnim ? `layer-anim-close` : "";
});

// 图标样式
const iconClass = computed(() => {
  return ["layui-layer-ico", `layui-layer-ico${props.icon}`];
});

// 打开事件
const open = function () {
  visible.value = true;
};

// 关闭事件
const close = function () {
  visible.value = false;
};

// 重置状态
const reset = function () {
  if (!first.value) {
    min.value = false;
    max.value = false;
    w.value = area.value[0];
    h.value = area.value[1];
    t.value = offset.value[0];
    l.value = offset.value[1];
    _w.value = area.value[0];
    _h.value = area.value[1];
    _t.value = offset.value[0];
    _l.value = offset.value[1];
    contentHeight.value = calculateContent(h.value, props.btn, type);
  }
  // 当 modelValue 为 false 的时候
  if (!props.modelValue) {
    emit("update:modelValue", true);
  }
};

const renderContent = function (name: string, content: any) {
  if (typeof content === 'function') {
    return content();
  }
  return content;
}


// 组件 触发
watch(
  () => props.modelValue,
  () => {
    visible.value = props.modelValue;
    if (visible.value) {
      if (first.value) {
        first.value = false;
        firstOpenDelayCalculation();
      } else {
        notFirstOpenLayerInit();
      }
    } else {
      beforeCloseSaveData();
    }
  },
  { deep: true }
);

// 函数 与 组件 都触发
watch(
  () => visible.value,
  () => {
    if (visible.value) {
      if (props.isFunction) {
        firstOpenDelayCalculation();
      }
      props.success();
    } else {
      props.end();
    }
  }
);

// 抛出事件
defineExpose({
  reset,
  open,
  close,
});
</script>

<template>
  <div
    v-if="visible && shade && !min"
    class="layui-layer-shade"
    style="background-color: rgb(0, 0, 0)"
    :style="{ zIndex: index, opacity: props.shadeOpacity }"
    @click="shadeHandle"
  ></div>
  <transition :enter-active-class="enterActiveClass" :leave-active-class="leaveActiveClass">
    <div
      :id="id"
      ref="layero"
      :class="[
        type === 0 ? 'layui-layer-dialog' : '',
        type === 1 ? 'layui-layer-page' : '',
        type === 2 ? 'layui-layer-iframe' : '',
        type === 3 ? 'layui-layer-loading' : '',
        isMessage ? 'layui-layer-msg' : '',
        isMessage && !icon ? 'layui-layer-hui' : '',
        'layui-layer-border',
        'layui-layer',
        skin,
      ]"
      :style="styles"
      v-if="visible"
      @click="setTopHandle"
    >
      <div
        class="layui-layer-title"
        style="cursor: move"
        v-if="title && type != 3"
      >{{ renderContent("title", props.title) }}</div>
      <div
        class="layui-layer-content"
        :style="{ height: contentHeight }"
        :class="[
          type === 3 ? `layui-layer-loading${load}` : '',
          icon ? 'layui-layer-padding' : '',
        ]"
      >
        <template v-if="type === 0 || type === 1">
          <i v-if="icon" :class="iconClass"></i>
          <slot v-if="slot.default"></slot>
          <template v-else>
            <template v-if="isHtmlFragment">
              <span v-html="renderContent('content', props.content)"></span>
            </template>
            <template v-else>{{ renderContent('content', props.content) }}</template>
          </template>
        </template>
        <iframe
          v-if="type === 2"
          scrolling="auto"
          allowtransparency="true"
          frameborder="0"
          :src="renderContent('content', props.content)"
          style="width: 100%; height: 100%"
        ></iframe>
      </div>
      <span class="layui-layer-setwin" v-if="type != 3">
        <a
          v-if="maxmin && !max"
          class="layui-layer-min"
          :class="[min ? 'layui-layer-ico layui-layer-maxmin' : '']"
          href="javascript:;"
          @click="minHandle"
        >
          <cite v-if="!min"></cite>
        </a>
        <a
          v-if="maxmin && !min"
          class="layui-layer-ico layui-layer-max"
          :class="[max ? 'layui-layer-maxmin' : '']"
          href="javascript:;"
          @click="maxHandle"
        ></a>
        <a
          v-if="closeBtn"
          class="layui-layer-ico layui-layer-close layui-layer-close1"
          href="javascript:;"
          @click="closeHandle"
        ></a>
      </span>
      <div
        v-if="((btn && btn.length > 0) || type === 0) && !isMessage"
        class="layui-layer-btn"
        :class="[`layui-layer-btn-${btnAlign}`]"
      >
        <template v-if="btn && btn.length > 0">
          <template v-for="(b, index) in btn" :key="index">
            <a :class="[`layui-layer-btn${index}`]" @click="b.callback(props.domId)">{{ b.text }}</a>
          </template>
        </template>
        <template v-else>
          <template v-if="type === 0">
            <a class="layui-layer-btn0" @click="yesHandle()">{{ yesText }}</a>
          </template>
        </template>
      </div>
      <span class="layui-layer-resize" v-if="resize && !max && !min"></span>
    </div>
  </transition>
</template>
